#include "bitstream.h"
#include "gerror.h"

using namespace std;

BitStream::BitStream() :
		bitIndex(7), pos(0), remainder(0), END(0)
{
}

BitStream::BitStream(std::fstream &file) :
		bitIndex(7), pos(0), remainder(0), END(0)
{
	file.seekg(0, ios::end);
	unsigned int len = file.tellg();
	file.seekg(0, ios::beg);
	bytes.reserve(len);
	while (bytes.size() < len)
	{
		bytes.push_back(file.get());
	}
}

char& BitStream::operator[](int index)
{
	return bytes[index];
}

int BitStream::get(int size)
{
	if (size < 1 || size > 32)
		throw GError("size should be between 1 and 32");

	if (END)
		return 0;

	int number = 0;
	char byte = bytes[pos];

	for (int i = 0; i < size; i++)
	{
		number <<= 1;
		number |= (byte >> bitIndex) & 1;
		bitIndex--;
		if (pos >= length() - 1 && bitIndex <= remainder - 1)
		{
			END = i + 1;
			return number;
		}
		if (bitIndex < 0)
		{
			bitIndex = 7;
			pos++;
			if (pos >= length())
			{
				END = i + 1;
				remainder = size - (i + 1);
				return number;
			}
			byte = bytes[pos];
		}
	}
	return number;
}

void BitStream::put(int number, int size)
{
	if (size < 1 || size > 32)
		throw GError("size should be between 1 and 32");

	if (pos >= length())
		bytes.push_back(0);

	for (int i = size - 1; i >= 0; i--)
	{
		bytes[pos] |= ((number >> i) & 1) << bitIndex;
		bitIndex--;
		if (bitIndex < 0)
		{
			bitIndex = 7;
			pos++;
			if (i > 0)
				bytes.push_back(0);
		}
	}
	remainder = (bitIndex + 1) % 8;
}

int BitStream::length()
{
	return (int) bytes.size();
}
